/* 
 * Copyright 2010 Ziya Guven Koylu, Ozge Koylu
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.koylu.caffein.model.clazz;

import java.sql.Connection;
import java.sql.ResultSet;
import java.util.Collection;
import java.util.List;
import java.util.Locale;

import javassist.util.proxy.ProxyObject;

import org.apache.commons.beanutils.PropertyUtils;

import com.koylu.caffein.Cascade;
import com.koylu.caffein.exception.ClazzConfigException;
import com.koylu.caffein.model.caffein.CaffeinConfig;
import com.koylu.caffein.model.clazz.validation.ValidateFor;
import com.koylu.caffein.model.clazz.validation.Validation;
import com.koylu.caffein.model.handler.ManyToOneContainerHandler;

public class OneToOne extends Relation {

	private String name;
	private String clazz;
	private Node parent;
	private Class<?> proxyClass;
	private List<Validation> validations;
	private String cascade = Cascade.ALL.name();

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getClazz() {
		return clazz;
	}

	public void setClazz(String clazz) {
		this.clazz = clazz;
	}
	
	public Clazz getForeignClazz(CaffeinConfig caffeinConfig) throws Exception {
		Clazz clazzz = caffeinConfig.getClazz(clazz);
		if(clazzz == null){
			throw new ClazzConfigException("clazz not found:"+clazz, this);
		}
		return clazzz;
	}

	public Node getParent() {
		return parent;
	}

	public void setParent(Node parent) {
		this.parent = parent;
	}

	public Collection<ForeignKey> getChildren() {
		return null;
	}

	public String getCascade() {
		return cascade;
	}

	public void setCascade(String cascade) {
		this.cascade = cascade.toUpperCase(Locale.ENGLISH);
	}

	public Object toObject(CaffeinConfig caffeinConfig, Object object, ResultSet resultSet) throws Exception {
		Object newObject = getProxyClass(caffeinConfig).newInstance();
		((ProxyObject) newObject).setHandler(new ManyToOneContainerHandler(caffeinConfig, getForeignClazz(caffeinConfig), object));
		return newObject;
	}
	
	public Object toEmptyObject(CaffeinConfig caffeinConfig, Clazz clazz) throws Exception {
		return null;
	}
	
	public Class<?> getProxyClass(CaffeinConfig caffeinConfig) throws Exception {
		if(proxyClass == null){
			proxyClass = getForeignClazz(caffeinConfig).getProxyClass(caffeinConfig);
		}
		return proxyClass;
	}

	public Class<?> toVirtualClass(CaffeinConfig caffeinConfig) throws Exception {
		return Object.class;
	}

	public Object copy(CaffeinConfig caffeinConfig, Object object) throws Exception {
		if(object != null){
			Object id = PropertyUtils.getProperty(object, getForeignClazz(caffeinConfig).getId().getName());
			Object newObject = getProxyClass(caffeinConfig).newInstance();
			((ProxyObject) newObject).setHandler(new ManyToOneContainerHandler(caffeinConfig, getForeignClazz(caffeinConfig), id));
			return newObject;			
		}
		return null;
	}
	
	public List<Validation> getValidations() {
		return validations;
	}

	public void setValidations(List<Validation> validations) {
		this.validations = validations;
	}
	
	public void validate(CaffeinConfig caffeinConfig, Clazz clazz, Object object, ValidateFor validateFor) throws Exception {
		if (validations != null) {
			for (Validation validation : validations) {
				validation.validate(caffeinConfig, clazz, this, object, validateFor);
			}
		}
	}
	
	public void save(CaffeinConfig caffeinConfig, Connection connection, Object object) throws Exception {
		Cascade _cascade = Cascade.valueOf(cascade);
		if (Cascade.ALL.equals(_cascade) || Cascade.SAVE.equals(_cascade)) {	
			Object foreignObject = PropertyUtils.getProperty(object, getName());
			if (foreignObject != null) {
				Clazz foreignClazz = getForeignClazz(caffeinConfig);
				PropertyUtils.setProperty(foreignObject, foreignClazz.getId().getName(), object);
				try {
					caffeinConfig.getCaffein().save(foreignClazz, connection, foreignObject);					
				} catch (Exception e) {
					throw new RuntimeException("oneToOne save error["+name+"]", e);
				}
			}
		}	
	}
	
	public void update(CaffeinConfig caffeinConfig, Connection connection, Object object) throws Exception {}
	
	public void delete(CaffeinConfig caffeinConfig, Clazz clazz, Connection connection, Object object) throws Exception {
		Cascade _cascade = Cascade.valueOf(cascade);
		if (Cascade.ALL.equals(_cascade) || Cascade.DELETE.equals(_cascade)) {
			Clazz foreignClazz = getForeignClazz(caffeinConfig);
			Object foreignObject = PropertyUtils.getProperty(object, name);
			try {
				caffeinConfig.getCaffein().delete(foreignClazz, connection, PropertyUtils.getProperty(foreignObject, foreignClazz.getId().getName()));					
			} catch (Exception e) {
				throw new RuntimeException("oneToOne delete error["+name+"]", e);
			}
		}	
	}
	
	public String getPath(CaffeinConfig caffeinConfig) throws Exception {
		return name;
	}
	
	public String toString() {
		return getClass().getName()+"["+name+"]";
	}
}